﻿#if ST
using System;
using System.Data;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Reflection;

using RAD.LightOne.Cache;
using RAD.LightOne.Interfaces;

namespace RAD.LightOne
{
    #region Documentation
    /// <summary>
    /// Classe que encapsula um objeto traduzido a uma versão intermediária, na verdade o pedaço do banco que esteja sendo utilizado
    /// em alguma operação de persistência e, assim como um banco de dados, esta classe é em si uma
    /// coleção de tabelas, representadas aqui por InternalObj que, assim como a classe que foi traduzida, contém dados de uma ou mais 
    /// tabelas.
    /// </summary>
    #endregion Documentation
    internal sealed class InternalObj : ICollection<Table>, ICloneable
    {
        #region Fields

        private List<Table> indexedTables;

        private Dictionary<string,Table> namedTables;

        private Type type;

        #endregion Fields

        #region Properties

        #region Documentation
        /// <summary>
        /// Retorna o tipo traduzido por este objeto intermediário.
        /// </summary>
        #endregion
        public Type Type
        {
            get
            {
                return this.type;
            }
        }

        #region Documentation
        /// <summary>
        /// Retorna o FullName da classe traduzida pelo onjeto intermediário.
        /// </summary>
        #endregion
        public string FullClassName
        {
            get
            {
                return this.type.FullName;
            }
        }

        #region Documentation
        /// <summary>
        /// Retorna um objeto de tabela eplo seu índice.
        /// </summary>
        /// <param name="index">índice, posição do objeto.</param>
        #endregion
        [System.Runtime.CompilerServices.IndexerName("GetItem")]
        public Table this[int index]
        {
            get
            {
                return this.indexedTables[index];
            }
        }

        #region Documentation
        /// <summary>
        /// Retorna um ítem de table por seu nome, ou seja o nome da tabela por ele mapeada.
        /// </summary>
        /// <param name="name">nome do item/tabela.</param>
        #endregion
        [System.Runtime.CompilerServices.IndexerName("GetItem")]
        public Table this[string key]
        {
            get
            {
                return this.namedTables[key];
            }
        }

        #endregion Properties

        #region Constructors

        #region Documentation
        /// <summary>
        /// Construtor onde se passa o tipo que a instânca deve traduzir.
        /// </summary>
        /// <param name="type">tipo a ser traduzido.</param>
        #endregion
        public InternalObj(Type type)
        {
            if (type == null) throw new ArgumentNullException("type");

            this.type = type;
            this.indexedTables = new List<Table>();
            this.namedTables = new Dictionary<string, Table>();
        }

        #endregion Constructors

        #region Methods

        #region Documentation
        /// <summary>
        /// Método que seta os valores contidos no objeto intermediário para a instância real da classe que ele mapeia.
        /// </summary>
        /// <param name="instance">instância que receberá os valores</param>
        /// <param name="trx">transação utilizada na recursividade de finds caso necessário.</param>
        #endregion Documentation
        public void SetValuesToInstance(MyObject instance, IDbTransaction trx)
        {
            DirectFieldMapper maped = new DirectFieldMapper(this);
            List<Type> types = new List<Type>();
            types.AddRange(MyObjectMetadataReader.GetHierarchy(instance.GetType(), typeof(MyObject)));

            foreach (Type t in types)
            {
                FieldInfo[] fieldInfos = t.GetFields(Global.BINDING_FLAGS);
                foreach (FieldInfo field in fieldInfos)
                {
                    if (DBFieldAttribute.IsDefined(field))
                    {
                        DBFieldAttribute attrib = (DBFieldAttribute)field.GetCustomAttributes(typeof(DBFieldAttribute), false)[0];
                        bool isIMyObject = (field.FieldType.GetInterface(typeof(IMyObject).FullName) != null);
                        if (((attrib.DataOperations & DataOperations.Select) == DataOperations.Select) && !isIMyObject)
                        {
                            try
                            {
                                if (maped[attrib.FieldName].Value is DBNull)
                                {
                                    field.SetValue(instance, null);
                                }
                                else
                                {
                                    #region Comments
                                    /*
                                     * Tratamento para enum nullable
                                     */
                                    #endregion
                                    bool isNullableEnum;
                                    try
                                    {
                                        isNullableEnum = field.FieldType.IsGenericType &&
                                                                field.FieldType.GetGenericTypeDefinition() == typeof(Nullable<>) &&
                                                                field.FieldType.GetGenericArguments()[0].IsSubclassOf(typeof(Enum));
                                    }
                                    catch { isNullableEnum = false; }
                                    if (isNullableEnum)
                                    {

                                        Type nullableType = field.FieldType.GetGenericArguments()[0];

                                        object value = Enum.ToObject(nullableType, maped[attrib.FieldName].Value);

                                        field.SetValue(instance, Activator.CreateInstance(field.FieldType, new object[] { value }));
                                    }
                                    else
                                    {
                                        //field.SetValue(instance, maped[attrib.FieldName].Value.GetType() == typeof(DBNull) ? null : maped[attrib.FieldName].Value);
                                        field.SetValue(instance, maped[attrib.FieldName].Value);
                                    }
                                }
                            }
                            catch (Exception except)
                            {
                                throw new ArgumentException("Erro occours on " + instance.GetType().Name + "." + field.Name + " when set a value, " + except.Message, except.InnerException);
                            }
                        }
                        else if (((attrib.DataOperations & DataOperations.Select) == DataOperations.Select) && isIMyObject)
                        {
                            try
                            {
                                MyObject found;
                                if (field.FieldType.IsInterface)
                                {
                                    found = MyObject.FindById(typeof(MyObject), new Guid(maped[attrib.FieldName].Value.ToString()), trx);
                                }
                                else
                                {
                                    found = MyObject.FindById(field.FieldType, new Guid(maped[attrib.FieldName].Value.ToString()), trx);
                                }
                                field.SetValue(instance, found);
                            }
                            catch
                            {
                                field.SetValue(instance, null);
                            }
                        }
                    }
                }
            }

        }

        #endregion Methods

        #region ICollection<DBTableItem> Members

        public void Add(Table item)
        {
            lock (this.indexedTables)
            {
                this.indexedTables.Add(item);
                this.namedTables.Add(item.Name, item);
                item.Index = this.indexedTables.IndexOf(item);
            }
        }

        public void Clear()
        {
            lock (this.indexedTables)
            {
                this.indexedTables.Clear();
                this.namedTables.Clear();
            }
        }

        public bool Contains(Table item)
        {
            return this.indexedTables.Contains(item);
        }

        public void CopyTo(Table[] array, int arrayIndex)
        {
           this.indexedTables.CopyTo(array,arrayIndex);
        }

        public void CopyTo(Table[] array)
        {
            this.indexedTables.CopyTo(array);
        }

        public int Count
        {
            get { return this.indexedTables.Count; }
        }

        public bool IsReadOnly
        {
            get { return true; }
        }

        public bool Remove(Table item)
        {
            lock (this.indexedTables)
            {
                this.namedTables.Remove(item.Name);
                return this.indexedTables.Remove(item);
            }
        }

        #endregion

        #region IEnumerable<DBTableItem> Members

        public IEnumerator<Table> GetEnumerator()
        {
            return this.indexedTables.GetEnumerator();
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return this.namedTables.GetEnumerator();
        }

        #endregion

        #region ICloneable Members

        public object Clone()
        {
            object newObj = this.MemberwiseClone() as object;
            return newObj;
        }

        #endregion
    }
}
#endif